package zsvn

import (
	"fmt"
	"net"
	"strings"
	"time"

	"gitee.com/zevenfly/goutil/zfind"
)

const (
	DEFAULT_PORT  = 3690
	BLOCK_BUF_LEN = 1024 * 8
)

// 配置
type Config struct {
	ServerAddr   string
	BasePath     string
	UserName     string
	UserPassword string
}

// client
type SvnClient struct {
	conn      net.Conn
	buf       []byte // 缓存
	totalLen  int    // 缓存中有效数据长度
	readedLen int    // 缓存中已经处理过的数据长度
	curChar   byte   // 当前处理的 byte

	latestRev int
	Config
}

const (
	VTYPE_ERROR = iota
	VTYPE_WORD
	VTYPE_NUM
	VTYPE_STRING
	VTYPE_LIST
)

// svn 协议中的对象
type Item struct {
	vtype int

	vword   string  // word 对象
	vnum    int     // 数字对象
	vstring string  // string 对象
	vlist   []*Item // 列表对象
}

func NewClient(cfg *Config) (*SvnClient, error) {
	if cfg == nil {
		return nil, fmt.Errorf("cfg nil")
	}

	client := &SvnClient{
		Config:    *cfg,
		buf:       make([]byte, BLOCK_BUF_LEN),
		totalLen:  0,
		readedLen: 0,
		curChar:   ' ',
	}

	return client, nil
}

// 获取一个字节内容。
func (sc *SvnClient) getChar() (byte, error) {
	if sc.readedLen < 0 || sc.readedLen > sc.totalLen {
		panic("something wrong")
	}
	if sc.readedLen == sc.totalLen { // 已经读完，需要重新读
		// 设置超时时间，读完后清除超时
		sc.conn.SetReadDeadline(time.Now().Add(time.Millisecond * 1000 * 3))
		defer sc.conn.SetReadDeadline(time.Time{})

		n, err := sc.conn.Read(sc.buf)
		if err != nil {
			return 0, err
		}
		if n <= 0 {
			return 0, fmt.Errorf("read from conn empty")
		}
		sc.totalLen = n
		sc.readedLen = 0
	}

	sc.curChar = sc.buf[sc.readedLen]
	sc.readedLen++
	return sc.curChar, nil
}

// 获取一个对象，支持嵌套。
func (sc *SvnClient) ScanItem(isNeedPreRead bool) (*Item, error) {
	if isNeedPreRead {
		if _, err := sc.getChar(); err != nil {
			return nil, err
		}
	}
	b := sc.curChar

	// number 或 string
	if '0' <= b && b <= '9' {
		var num uint32 = uint32(uint8(b) - uint8('0'))

		for {
			b, err := sc.getChar()
			if err != nil {
				return nil, err
			}

			// num 结束
			if b == ' ' {
				return &Item{
					vtype: VTYPE_NUM,
					vnum:  int(num),
				}, nil
			}

			// string
			if b == ':' {
				if num > 1024*8 {
					panic("something wrong")
				}

				vstring := ""
				if num > 0 {
					var i uint32
					var bs []byte

					for i = 0; i < num; i++ {
						b2, err := sc.getChar()
						if err != nil {
							return nil, err
						}
						bs = append(bs, b2)
					}
					vstring = string(bs)
				}

				if b, err := sc.getChar(); err != nil {
					return nil, err
				} else if b != ' ' {
					return nil, fmt.Errorf("except space but not match")
				}

				return &Item{
					vtype:   VTYPE_STRING,
					vstring: vstring,
				}, nil
			}

			if '0' <= b && b <= '9' {
				num = num*10 + uint32(uint8(b)-uint8('0'))
				continue
			}

			return nil, fmt.Errorf("unexcepted char")
		}

		panic("something wrong")
	}

	// word
	if 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z' {
		var bs []byte = []byte{b}

		for {
			b, err := sc.getChar()
			if err != nil {
				return nil, err
			}

			if b == ' ' {
				return &Item{
					vtype: VTYPE_WORD,
					vword: string(bs),
				}, nil
			}

			if 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z' ||
				'0' <= b && b <= '9' ||
				b == '-' {
				bs = append(bs, b)
				continue
			}

			return nil, fmt.Errorf("unexcepted char")
		}

		panic("something wrong")
	}

	// list
	if b == '(' {
		return sc.ScanList(false)
	}

	return nil, fmt.Errorf("unkown token: %v|%c", b, b)
}

// 获取列表对象。
func (sc *SvnClient) ScanList(isNeedPreRead bool) (*Item, error) {
	if isNeedPreRead {
		if _, err := sc.getChar(); err != nil {
			return nil, err
		}
	}
	if sc.curChar != '(' {
		return nil, fmt.Errorf("except ( but not match")
	}
	if b, err := sc.getChar(); err != nil {
		return nil, err
	} else if b != ' ' {
		return nil, fmt.Errorf("except space but not match")
	}

	var vlist []*Item

	for {
		// item 结束
		b, err := sc.getChar()
		if err != nil {
			return nil, err
		}

		if b == ')' {
			if b, err := sc.getChar(); err != nil {
				return nil, err
			} else if b != ' ' {
				return nil, fmt.Errorf("except space but not match")
			}

			return &Item{
				vtype: VTYPE_LIST,
				vlist: vlist,
			}, nil
		}

		if item, err := sc.ScanItem(false); err != nil {
			return nil, err
		} else {
			vlist = append(vlist, item)
		}
	}

	panic("something wrong")
}

// 获取结果。
// 注意：只检查 list 的第 1 个元素是否为 success
func (sc *SvnClient) ScanResult(isNeedPreRead bool) (bool, error) {
	item, err := sc.ScanList(isNeedPreRead)
	if err != nil {
		return false, err
	}

	// list 检查
	if item.vtype != VTYPE_LIST {
		return false, fmt.Errorf("item is not list")
	} else if len(item.vlist) < 1 {
		return false, fmt.Errorf("list ele count error")
	}

	// word 检查
	item = item.vlist[0]
	if item.vtype != VTYPE_WORD {
		return false, fmt.Errorf("item type error")
	}
	return item.vword == "success", nil
}

// 获取 challenge
func (sc *SvnClient) ScanChallenge(isNeedPreRead bool) (string, error) {
	item, err := sc.ScanList(isNeedPreRead)
	if err != nil {
		return "", err
	}

	// list 检查
	if item.vtype != VTYPE_LIST {
		return "", fmt.Errorf("item is not list")
	} else if len(item.vlist) < 2 {
		return "", fmt.Errorf("list ele count error")
	}

	// step
	if item2 := item.vlist[0]; item2.vtype != VTYPE_WORD {
		return "", fmt.Errorf("item type error")
	} else if item2.vword != "step" {
		return "", fmt.Errorf("check step error")
	}

	// challenge
	item = item.vlist[1]
	if item.vtype != VTYPE_LIST {
		return "", fmt.Errorf("item is not list")
	} else if len(item.vlist) < 1 {
		return "", fmt.Errorf("list ele count error")
	}

	// string
	item = item.vlist[0]
	if item.vtype != VTYPE_STRING {
		return "", fmt.Errorf("item type error")
	}
	challenge := item.vstring // <3180599559953700532.1670222064176344@svn3>
	if !strings.HasPrefix(challenge, "<") || !strings.HasSuffix(challenge, ">") {
		return "", fmt.Errorf("challenge format error:%s", challenge)
	}
	return challenge, nil
}

// 获取 latestRev
func (sc *SvnClient) ScanLatestRev(isNeedPreRead bool) (int, error) {
	item, err := sc.ScanList(isNeedPreRead)
	if err != nil {
		return 0, err
	}

	// list 检查
	if item.vtype != VTYPE_LIST {
		return 0, fmt.Errorf("item is not list")
	} else if len(item.vlist) < 2 {
		return 0, fmt.Errorf("list ele count error")
	}

	// success
	if item2 := item.vlist[0]; item2.vtype != VTYPE_WORD {
		return 0, fmt.Errorf("item type error")
	} else if item2.vword != "success" {
		return 0, fmt.Errorf("check success error")
	}

	// latestRev
	item = item.vlist[1]
	if item.vtype != VTYPE_LIST {
		return 0, fmt.Errorf("item is not list")
	} else if len(item.vlist) < 1 {
		return 0, fmt.Errorf("list ele count error")
	}

	// num
	item = item.vlist[0]
	if item.vtype != VTYPE_NUM {
		return 0, fmt.Errorf("item type error")
	}
	latestRev := item.vnum
	if latestRev <= 0 {
		return 0, fmt.Errorf("latestRev num error")
	}
	return latestRev, nil
}

// 登录
func (sc *SvnClient) Login() error {
	if conn, err := net.DialTimeout("tcp", sc.Config.ServerAddr, time.Millisecond*3000); err != nil {
		return err
	} else {
		sc.conn = conn
	}

	// 退出时检查
	var loginOk bool
	defer func() {
		if !loginOk && sc.conn != nil {
			sc.conn.Close()
			sc.conn = nil
		}
	}()

	// greet
	if result, err := sc.ScanResult(true); err != nil {
		return err
	} else if !result {
		return fmt.Errorf("greet failed")
	}

	// todo
	svnPath := sc.Config.BasePath
	msg := fmt.Sprintf("( 2 ( edit-pipeline svndiff1 absent-entries depth mergeinfo log-revprops ) %d:%s 33:SVN/1.9.6 (x64-microsoft-windows) ( 23:TortoiseSVN-1.9.6.27867 ) ) ", len(svnPath), svnPath)
	sc.conn.Write([]byte(msg))

	// CRAM-MD5
	if result, err := sc.ScanResult(true); err != nil {
		return err
	} else if !result {
		return fmt.Errorf("get CRAM-MD5 failed")
	}

	msg = "( CRAM-MD5 ( ) ) "
	sc.conn.Write([]byte(msg))

	// challenge
	challenge, err := sc.ScanChallenge(true)
	if err != nil {
		return err
	} else if challenge == "" {
		return fmt.Errorf("get challenge failed")
	}

	tstr := fmt.Sprintf("%s %s", sc.Config.UserName, encry(challenge, sc.Config.UserPassword))
	msg = fmt.Sprintf("%d:%s ", len(tstr), tstr)
	sc.conn.Write([]byte(msg))

	// 检查登陆结果
	if result, err := sc.ScanResult(true); err != nil { // 第 1 个 item
		return err
	} else if !result {
		return fmt.Errorf("login failed")
	}
	if result, err := sc.ScanResult(true); err != nil { // 第 2 个 item
		return err
	} else if !result {
		return fmt.Errorf("login failed")
	}

	// get-latest-rev
	msg = "( get-latest-rev ( ) ) "
	sc.conn.Write([]byte(msg))

	if result, err := sc.ScanResult(true); err != nil { // 第 1 个 item
		return err
	} else if !result {
		return fmt.Errorf("get-latest-rev failed")
	}
	latestRev, err := sc.ScanLatestRev(true) // 第 2 个 item
	if err != nil {
		return err
	} else if latestRev <= 0 {
		return fmt.Errorf("get-latest-rev failed")
	}
	sc.latestRev = latestRev

	loginOk = true
	return nil
}

// 遍历路径下的文件和目录。
func (sc *SvnClient) Walk(path string, cb zfind.WalkCallBack) error {
	if cb == nil {
		return fmt.Errorf("cb nil")
	}
	if sc.conn == nil || sc.latestRev < 1 {
		return fmt.Errorf("has not login")
	}

	// reparent
	msg := fmt.Sprintf("( reparent ( %d:%s ) ) ", len(path), path)
	sc.conn.Write([]byte(msg))

	if result, err := sc.ScanResult(true); err != nil { // 第 1 个 item
		return err
	} else if !result {
		return fmt.Errorf("reparent failed")
	}
	if result, err := sc.ScanResult(true); err != nil { // 第 2 个 item
		return err
	} else if !result {
		return fmt.Errorf("reparent failed")
	}

	/*
		// get-locks
		msg = "( get-locks ( 0: ( immediates ) ) ) "
		sc.conn.Write([]byte(msg))

		if result, err := sc.ScanResult(true); err != nil { // 第 1 个 item
			return err
		} else if !result {
			return fmt.Errorf("reparent failed")
		}
		if result, err := sc.ScanResult(true); err != nil { // 第 2 个 item
			return err
		} else if !result {
			return fmt.Errorf("reparent failed")
		}
	*/

	// get-dir
	//msg = "( get-dir ( 0: ( 717699 ) false true ( kind size has-props created-rev time last-author ) false ) ) "
	msg = fmt.Sprintf("( get-dir ( 0: ( %d ) false true ( kind ) false ) ) ", sc.latestRev)
	sc.conn.Write([]byte(msg))

	if result, err := sc.ScanResult(true); err != nil { // 第 1 个 item
		return err
	} else if !result {
		return fmt.Errorf("get-dir failed")
	}
	item, err := sc.ScanList(true)
	if err != nil {
		return err
	}

	// list 检查
	if item.vtype != VTYPE_LIST {
		return fmt.Errorf("item is not list")
	} else if len(item.vlist) < 2 {
		return fmt.Errorf("list ele count error")
	}

	// success
	if item2 := item.vlist[0]; item2.vtype != VTYPE_WORD {
		return fmt.Errorf("item type error")
	} else if item2.vword != "success" {
		return fmt.Errorf("check success error")
	}

	// dir-files
	item = item.vlist[1]
	if item.vtype != VTYPE_LIST {
		return fmt.Errorf("item is not list")
	} else if len(item.vlist) < 3 {
		return fmt.Errorf("list ele count error")
	}

	//
	item = item.vlist[2]
	if item.vtype != VTYPE_LIST {
		return fmt.Errorf("item is not list")
	}
	for i := 0; i < len(item.vlist); i++ {
		item2 := item.vlist[i]
		if item2.vtype != VTYPE_LIST {
			return fmt.Errorf("item is not list")
		} else if len(item2.vlist) < 3 {
			return fmt.Errorf("list ele count error")
		}

		itemName := item2.vlist[0]
		if itemName.vtype != VTYPE_STRING {
			return fmt.Errorf("name is not string")
		}
		xname := itemName.vstring

		itemType := item2.vlist[1]
		if itemType.vtype != VTYPE_WORD {
			return fmt.Errorf("file type is not word")
		}
		if word := itemType.vword; word == "dir" {
			//是目录
			//dir = dir + "/"
			if goon, err := cb(xname, zfind.FILETYPE_DIR, nil); !goon || err != nil {
				return err
			}
		} else if word == "file" {
			//是文件
			if goon, err := cb(xname, zfind.FILETYPE_FILE, nil); !goon || err != nil {
				return err
			}
		} else {
			return fmt.Errorf("unkown file type string")
		}
	}

	return nil
}

// 关闭。
func (sc *SvnClient) Close() {
	if sc.conn != nil {
		sc.conn.Close()
		sc.conn = nil
	}
}
